#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <memory>
#include "Mutex.hpp"

class SyncStrategy
{
public:
    virtual ~SyncStrategy() {}
    virtual void SyncLog(const std::string &logmessage) = 0;
};

class ConsoleSyncStrategy : public SyncStrategy
{
public:
    void SyncLog(const std::string &logmessage) override
    {
        LockGuard lg(&_mutex); // 加锁保护
        std::cout << logmessage << std::endl;
    }
    ~ConsoleSyncStrategy() override
    {
    }

private:
    Mutex _mutex;
};

static const std::string dir_name = "Log/";
static const std::string file_name = "logger";
class FileSyncStrategy : public SyncStrategy
{
public:
    FileSyncStrategy() : _dir_name(dir_name), _file_name(file_name)
    {
        LockGuard lg(&_mutex); // 保证判断目录是否存在和创建目录操作的原子性，不能被干扰
        if (std::filesystem::exists(_dir_name))
            return;
        try
        {
            std::filesystem::create_directories(_dir_name);
        }
        catch (const std::filesystem::filesystem_error &e)
        {
            std::cerr << e.what() << '\n';
        }
    }
    void SyncLog(const std::string &logmessage) override
    {
        // 已经创建好相关目录和文件，打开特定文件后，做刷新即可
        std::string target = _dir_name + _file_name;
        LockGuard lg(&_mutex);
        std::ofstream out(target.c_str(), std::ios::app);
        if (!out.is_open())
        {
            std::cerr << "open file error" << std::endl;
            return;
        }
        out << logmessage << std::endl;
        out.close();
    }
    ~FileSyncStrategy() override
    {
    }

private:
    std::string _dir_name;  // 记录目录名
    std::string _file_name; // 记录文件名
    Mutex _mutex;
};

enum class LogLevel
{
    INFO,
    DEBUG,
    WARNING,
    ERROR,
    FATAL
};
class Logger
{
public:
    // 相关日志的信息均为实时获取的
    Logger()
    {
    }
    void EnableConsoleLogStrategy()
    {
        _strategy = std::make_unique<ConsoleSyncStrategy>();
    }
    void EnableFileLogStrategy()
    {
        _strategy = std::make_unique<FileSyncStrategy>();
    }

    class LogMessage
    {
    private:
        std::string GetCurrentTime()
        {
            time_t time_stamp = time(nullptr); // 获取时间戳
            struct tm local_time;
            localtime_r(&time_stamp, &local_time);
            char buffer[128];
            // 使用C接口,比较方便进行格式控制:年月日,时分秒
            snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
                     local_time.tm_year + 1900,
                     local_time.tm_mon + 1,
                     local_time.tm_mday,
                     local_time.tm_hour,
                     local_time.tm_min,
                     local_time.tm_sec);
            return buffer;
        }

        std::string LevelToString(LogLevel level)
        {
            switch (level)
            {
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::DEBUG:
                return "DEBUG";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";
            default:
                return "";
            }
        }

    public:
        LogMessage(LogLevel level, const std::string &file_name, int line, Logger &logger)
            : _curr_time(GetCurrentTime()), _level(level), _pid(getpid()), _file_name(file_name),
              _line(line), _logger(logger)
        {
            // 收集完必备信息后
            std::stringstream ss;
            ss << "[" << _curr_time << "] " 
               << "[" << LevelToString(_level) << "] " 
               << "[" << _pid << "] " 
               << "[" << _file_name << "] " 
               << "[" << _line << "] " 
               << "- ";
            _logmessage = ss.str();
        }

        template<typename T>
        LogMessage& operator<<(const T& input)
        {
            std::stringstream ss;//复用，不需要自己再做重载
            ss << input;
            _logmessage += ss.str();
            return *this;
        }

        ~LogMessage()
        {
            _logger._strategy->SyncLog(_logmessage);
        }
    private:
        std::string _curr_time;
        LogLevel _level;
        pid_t _pid;
        std::string _file_name;
        int _line;
        std::string _logmessage;
        Logger &_logger;
    };

    LogMessage operator()(LogLevel level,std::string file_name,int line)
    {
        return LogMessage(level,file_name,line,*this);
    }
    ~Logger()
    {
    }

private:
    std::unique_ptr<SyncStrategy> _strategy;
};

static Logger logger;

#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnableFileLogStrategy()
#define LOG(level) logger(level,__FILE__,__LINE__)